#!/bin/bash
# https://github.com/containerd/containerd/releases/download/v1.7.20/containerd-1.7.20-linux-amd64.tar.gz
# https://github.com/opencontainers/runc/releases/download/v1.1.13/runc.amd64
# https://github.com/containernetworking/plugins/releases/download/v1.5.1/cni-plugins-linux-amd64-v1.5.1.tgz
# https://github.com/containerd/nerdctl/releases/download/v1.7.6/nerdctl-1.7.6-linux-amd64.tar.gz
# https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.31.1/crictl-v1.31.1-linux-amd64.tar.gz
# https://github.com/moby/buildkit/releases/download/v0.15.2/buildkit-v0.15.2.linux-amd64.tar.gz
set -euo pipefail

#****** package vars ******#
# if var is empty,the pack will not install,but you have to set CONTAINERD_PACK and RUNC_PACK
CONTAINERD_PACK="containerd-1.7.20-linux-amd64.tar.gz"
CNI_PACK="cni-plugins-linux-amd64-v1.5.1.tgz"
RUNC_PACK="runc.amd64-v1.1.13"
CRICTL_PACK="crictl-v1.31.1-linux-amd64.tar.gz"
NERDCTL_PACK="nerdctl-1.7.6-linux-amd64.tar.gz"
BUILDKIT_PACK="buildkit-v0.15.2.linux-amd64.tar.gz"
#****** directory vars ******#
# containerd vars
CONTAINERD_INSTALL_PATH="/usr/local/containerd"
CONTAINERD_CONFIG_PATH="/usr/local/containerd/conf"
CONTAINERD_DATA_PATH="/data/containerd"
CONTAINERD_STATE_PATH="/run/containerd"       # socket file also in here
# runc vars
RUNC_INSTALL_BIN_PATH="/usr/local/containerd/bin"
# crictl vars
CRICTL_INSTALL_BIN_PATH="/usr/local/bin"      # suggest don't change it
CRICTL_CONF="/etc/crictl.yaml"                # suggest don't change it

# nerdctl vars
NERDCTL_INSTALL_BIN_PATH="/usr/local/bin"     # suggest don't change it

# buildkit vars
BUILDKIT_INSTALL_PATH="/usr/local/buildkit"
BUILDKIT_DATA_PATH="/data/buildkit"
BUILDKIT_STATE_PATH="/run/buildkit"                        # socket file localtion

# cni plugins var
CNI_BIN_PATH="/usr/local/containerd/cni/bin"
CNI_CONF_PATH="/usr/local/containerd/cni/net.d"
#****** proxy vars  ******#
CONTAINERD_HTTP_PROXY="http://192.168.50.5:7890"
CONTAINERD_HTTPS_PROXY="http://192.168.50.5:7890"
CONTAINERD_NO_PROXY="127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"


#****** functions ******#
color() {
    local message="$1"
    local status="$2"
    local terminal_width
    terminal_width=$(tput cols)
    local middle_column=$(( terminal_width / 2))
    local SETCOLOR_SUCCESS="\033[1;32m"  # 绿色
    local SETCOLOR_FAILURE="\033[1;31m"  # 红色
    local SETCOLOR_WARNING="\033[1;33m"  # 黄色
    local SETCOLOR_NORMAL="\033[0m"      # 重置颜色
    printf '%s\n\033[%sG' "${message}"  "${middle_column}"
    # Print the status message
    printf "["
    case ${status} in
        success|0)
            echo -en  "${SETCOLOR_SUCCESS}  OK  "
            ;;
        failure|1)
            echo -en  "${SETCOLOR_FAILURE}FAILED"
            ;;
        *)
            echo -en "${SETCOLOR_WARNING}WARNING"
            ;;
    esac
    echo -e "${SETCOLOR_NORMAL}]"
}

create_dir () {
    local i
    for i in "$@";do
        if [ ! -d "$i" ];then
            mkdir -pv "$i" && color "$i dir is created" 0
        else
            color "$i dir is existed." 2
        fi
    done
}

judge_current_user () {
    if [ "$(id -u)" = "0" ];then
        color "current user is administrator" 0
    else
        color "current user is not administrator" 1
        exit
    fi
}

judge_file_exist () { 
    local i=0
    for i in "$@";do
        if [ -e "$i" ];then
            color "current dir $i exist." 0
        else
            color "current dir don't exist $i" 1
            exit
        fi
    done
}

install_containerd_and_runc () {
    create_dir "$CONTAINERD_INSTALL_PATH" "$CONTAINERD_CONFIG_PATH" "$CONTAINERD_DATA_PATH" "$CONTAINERD_STATE_PATH" "$RUNC_INSTALL_BIN_PATH"
    tar -xvf "$CONTAINERD_PACK" -C "$CONTAINERD_INSTALL_PATH"
    \cp $RUNC_PACK $RUNC_INSTALL_BIN_PATH/runc
    # generate containerd config file
    $CONTAINERD_INSTALL_PATH/bin/containerd config default > $CONTAINERD_CONFIG_PATH/config.toml
    # change data dir and state dir and sock file,and runc path
    sed -i -e "/^root = /c root = \"$CONTAINERD_DATA_PATH\"" \
           -e "/^state = /c state = \"$CONTAINERD_STATE_PATH\"" \
           -e "/\[grpc\]/,+1s@address = \".*\"@address = \"$CONTAINERD_STATE_PATH/containerd.sock\"@" \
           -e "/containerd.runtimes.runc.options/,+1s@BinaryName = \".*\"@BinaryName = \"$RUNC_INSTALL_BIN_PATH/runc\"@" \
           $CONTAINERD_CONFIG_PATH/config.toml
    # user SystemdCgroup
    sed -i "/SystemdCgroup/s/false/true/" $CONTAINERD_CONFIG_PATH/config.toml

    chown -R 0:0 $CONTAINERD_INSTALL_PATH $RUNC_INSTALL_BIN_PATH $CONTAINERD_CONFIG_PATH
    chmod 755 $CONTAINERD_INSTALL_PATH/bin/* $RUNC_INSTALL_BIN_PATH/runc
    color "containerd and runc install success." 0
}

install_cni () {
    create_dir "$CNI_BIN_PATH" "$CNI_CONF_PATH"
    tar -xvf $CNI_PACK -C $CNI_BIN_PATH
    sed -i -e "/plugins.\"io.containerd.grpc.v1.cri\".cni]/,+2s@bin_dir = \".*\"@bin_dir = \"$CNI_BIN_PATH\"@" \
           -e "/plugins.\"io.containerd.grpc.v1.cri\".cni]/,+2s@conf_dir = \".*\"@conf_dir = \"$CNI_CONF_PATH\"@" \
           $CONTAINERD_CONFIG_PATH/config.toml
    chown -R 0:0 $CNI_BIN_PATH
    chmod 755 $CNI_BIN_PATH/*
    color "cni  plugin  install success." 0
}

install_crictl () {
    create_dir $CRICTL_INSTALL_BIN_PATH ${CRICTL_CONF%/*}
    tar -xvf $CRICTL_PACK -C $CRICTL_INSTALL_BIN_PATH/
    chown 0:0 $CRICTL_INSTALL_BIN_PATH/crictl
    chmod 755 $CRICTL_INSTALL_BIN_PATH/crictl
    cat > $CRICTL_CONF <<__EOF__
runtime-endpoint: $CONTAINERD_STATE_PATH/containerd.sock
image-endpoint: $CONTAINERD_STATE_PATH/containerd.sock
timeout: 10
debug: true
__EOF__
    color "$CRICTL_INSTALL_BIN_PATH/crictl install success" 0
}

install_nerdctl () {
    create_dir $NERDCTL_INSTALL_BIN_PATH
    tar -xvf $NERDCTL_PACK -C $NERDCTL_INSTALL_BIN_PATH/ nerdctl
    chown 0:0 $NERDCTL_INSTALL_BIN_PATH/nerdctl
    chmod 755 $NERDCTL_INSTALL_BIN_PATH/nerdctl

    cat > /etc/profile.d/containerd_nerdctl.sh <<__EOF__
export CNI_PATH="$CNI_BIN_PATH"
export NETCONFPATH="$CNI_CONF_PATH"
export CONTAINERD_ADDRESS="$CONTAINERD_STATE_PATH/containerd.sock"
export BUILDKIT_HOST="unix://$BUILDKIT_STATE_PATH/buildkitd.sock"
export PATH="\$PATH:$CONTAINERD_INSTALL_PATH/bin:$RUNC_INSTALL_BIN_PATH:$BUILDKIT_INSTALL_PATH/bin"
__EOF__
    echo -e "need run \E[33;1m source /etc/profile.d/containerd_nerdctl.sh\E[0m"
    color "$NERDCTL_INSTALL_BIN_PATH/nerdctl install success" 0
}

install_buildkit () {
    create_dir $BUILDKIT_INSTALL_PATH $BUILDKIT_DATA_PATH $BUILDKIT_STATE_PATH
    tar -xvf $BUILDKIT_PACK -C $BUILDKIT_INSTALL_PATH/
    chown 0:0 $BUILDKIT_INSTALL_PATH/bin/*
    chmod 755 $BUILDKIT_INSTALL_PATH/bin/*
}

start_buildkit () {
    if systemctl status buildkit.socket &> /dev/null;then
        systemctl stop buildkit.socket
    fi
    if systemctl status buildkit.service &> /dev/null;then
        systemctl stop buildkit.service
    fi

    cat >  /lib/systemd/system/buildkit.socket <<__EOF__
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit

[Socket]
ListenStream=$BUILDKIT_STATE_PATH/buildkitd.sock
SocketMode=0660

[Install]
WantedBy=sockets.target
__EOF__

    cat >  /lib/systemd/system/buildkit.service <<__EOF__
[Unit]
Description=BuildKit
Requires=buildkit.socket
After=buildkit.socket
Documentation=https://github.com/moby/buildkit

[Service]
Type=notify
ExecStart=$BUILDKIT_INSTALL_PATH/bin/buildkitd --addr fd:// \
          --root "$BUILDKIT_DATA_PATH" \
          --containerd-worker-addr  "$CONTAINERD_STATE_PATH/containerd.sock" \
          --containerd-cni-binary-dir "$CNI_BIN_PATH"

[Install]
WantedBy=multi-user.target
__EOF__
    systemctl daemon-reload
    systemctl enable buildkit.socket buildkit.service
    systemctl start buildkit.socket buildkit.service \
    && color "buildkit.socket,buildkit.service start sucess!" 0 \
    || { color "buildkit.socket,buildkit.service  start fail." 1; exit; }
}

start_containerd () {
    if systemctl status containerd.service &> /dev/null;then
        systemctl stop containerd.service
    fi
    cat > /lib/systemd/system/containerd.service <<EOF
# https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
# Copyright The containerd Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
Environment="HTTP_PROXY=$CONTAINERD_HTTP_PROXY"
Environment="HTTPS_PROXY=$CONTAINERD_HTTPS_PROXY"
Environment="NO_PROXY=$CONTAINERD_NO_PROXY"
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$CONTAINERD_INSTALL_PATH/bin:$RUNC_INSTALL_BIN_PATH:$BUILDKIT_INSTALL_PATH/bin"
ExecStartPre=-/sbin/modprobe overlay
ExecStartPre=-/sbin/modprobe br_netfilter
ExecStart=$CONTAINERD_INSTALL_PATH/bin/containerd -c $CONTAINERD_CONFIG_PATH/config.toml

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
    systemctl enable containerd.service
    systemctl start containerd.service \
    && color "containerd.service start sucess!" 0 \
    || { color "containerd.service  start fail." 1; exit; }
}

set_kernel_args () {
    {
        sysctl -a | awk '/net.ipv4.ip_forward =/{if($3 < 1){print "net.ipv4.ip_forward = 1"}}'
        sysctl -a | awk '/net.bridge.bridge-nf-call-ip6tables =/{if($3 < 1){print "net.bridge.bridge-nf-call-ip6tables = 1"}}'
        sysctl -a | awk '/net.bridge.bridge-nf-call-iptables =/{if($3 < 1){print "net.bridge.bridge-nf-call-iptables = 1"}}'
    } >> /etc/sysctl.conf
    sysctl -p
    color "linux kernel args set complete." 0
}

main () {
    judge_current_user
    judge_file_exist "$CONTAINERD_PACK" "$RUNC_PACK"
    install_containerd_and_runc
    [ -e "$CNI_PACK" ] && install_cni || color "CNI_PACK is not exist or set." 2
    start_containerd
    set_kernel_args

    [ -e "$CRICTL_PACK" ] && install_crictl || color "CRICTL_PACK is not exist or set." 2
    [ -e "$BUILDKIT_PACK" ] && { install_buildkit;start_buildkit; }|| color "BUILDKIT_PACK is not exist or set." 2
    [ -e "$NERDCTL_PACK" ] && install_nerdctl || color "NERDCTL_PACK is not exist or set." 2
    

}

main